/*
 * Copyright Konstantin Triger <kostat@gmail.com> 
 * 
 * This file is part of Jaque - JAva QUEry library <http://code.google.com/p/jaque/>.
 * 
 * Jaque is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jaque is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package jaque;

import java.lang.reflect.Method;

import jaque.expressions.*;
import jaque.functions.*;
import static jaque.expressions.Expression.*;

abstract class Group<Key, T> implements Iterable<T> {
	protected Group(Key key) {
		Key = key;
	}

	public final Key Key;
}

final class Join2<Outer, Inner> {
	public Join2(Outer outer, Inner inner) {
		Outer = outer;
		Inner = inner;
	}

	public final Outer Outer;
	public final Inner Inner;
}

public final class DynamicQuery {

	private static final Method _where;
	private static final Method _skip;
	private static final Method _select;
	private static final Method _selectMany;

	static {
		try {
			_where = Query.class.getDeclaredMethod("where", new Class<?>[] {
					Function.class, Iterable.class });
			_skip = Query.class.getDeclaredMethod("skip", new Class<?>[] {
					Integer.TYPE, Iterable.class });
			_select = Query.class.getDeclaredMethod("select", new Class<?>[] {
					Function.class, Iterable.class });
			_selectMany = Query.class.getDeclaredMethod("selectMany",
					new Class<?>[] { Function.class, Iterable.class });
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}

	public static <S> Iterable<S> from(Iterable<S> source) {
		return source;

	}

	public static <S> Queryable<S> toQueryable(Iterable<S> source) {
		return new IterableQueryable<S>(null, source);

	}

	@SuppressWarnings("unchecked")
	public static <E> Queryable<E> where(
			final Function<Boolean, ? super E> predicate,
			final Queryable<E> source) {
		LambdaExpression<?> lambda = LambdaExpression.parse(predicate);
		Class<E> eType = source.getElementType();
		if (eType == null)
			eType = (Class<E>) lambda.getParameters().get(0).getResultType();
		Expression e = invoke(null, _where, quote(lambda), source
				.getExpression());
		return source.getFactory().createQueryable(eType, e);
	}

	@SuppressWarnings("unchecked")
	public static <T, Result> Queryable<Result> select(
			final Function<? extends Result, ? super T> transform,
			final Queryable<T> source) {
		LambdaExpression<?> lambda = LambdaExpression.parse(transform);
		Expression e = invoke(null, _select, quote(lambda), source
				.getExpression());
		return (Queryable<Result>) source.getFactory().createQueryable(
				lambda.getResultType(), e);
	}

	@SuppressWarnings("unchecked")
	public static <T, Result> Queryable<Result> selectMany(
			final Function<Iterable<Result>, ? super T> transform,
			final Queryable<T> source) {
		LambdaExpression<?> lambda = LambdaExpression.parse(transform);
		Expression e = invoke(null, _selectMany, quote(lambda), source
				.getExpression());
		return (Queryable<Result>) source.getFactory().createQueryable(null, e);
	}

	public static <T> Queryable<T> skip(final int count,
			final Queryable<T> source) {
		Expression e = invoke(null, _skip, constant(count, Integer.TYPE),
				source.getExpression());
		return source.getFactory().createQueryable(source.getElementType(), e);
	}

	private DynamicQuery() {
	}

	// public static <T, R> Iterable<R> from(
	// Function<Iterable<?>, LambdaExpression<Function<Iterable<?>, Iterable<?
	// extends T>>>> queryable,
	// Function<Iterable<R>, Iterable<? extends T>> /*
	// * {Iterable<?
	// * extends T> =>
	// * Iterable<R> }
	// */selector) {
	// try {
	// return (Iterable<R>)queryable.invoke(
	// (LambdaExpression<Function<Iterable<?>, Iterable<? extends T>>>)
	// (Object)LambdaExpression.parse(selector));
	// } catch (Throwable e) {
	// throw new RuntimeException(e);
	// }
	// }

	// @SuppressWarnings("unchecked")
	// public static <T, R> Iterable<R> from(Queryable<T> source,
	// Function<Iterable<R>, ? super Iterable<? extends T>> /*
	// * {Iterable<?
	// * extends
	// * T> =>
	// * Iterable<R> }
	// */selector) {
	// Expression e = LambdaExpression.parse(selector);
	// e = e.apply(QueryableExpressionVisitor.Instance);
	// e = e.apply(new ExpressionInliner());
	// // e = e.apply(LambdaExpressionReducer.Instance);
	// return null;// source
	// // .invoke((LambdaExpression<Function<Iterable<R>, Iterable<? extends
	// // T>>>) e);
	// }
	//
	// public static <T, R> Iterable<R> from(Iterable<? extends T> source,
	// Function<Iterable<R>, ? super Iterable<? extends T>> /*
	// * {Iterable<?
	// * extends
	// * T> =>
	// * Iterable<R> }
	// */selector) {
	// try {
	// return selector.invoke(source);
	// } catch (RuntimeException e) {
	// throw e;
	// } catch (Throwable e) {
	// throw new RuntimeException(e);
	// }
	// }
	//
	// public static <T, S, R> Function<Iterable<R>, ? super Iterable<? extends
	// T>> from(
	// final Function<Iterable<S>, ? super T> /*
	// * { T => Iterable<?
	// * extends S> }
	// */transform,
	// final Function<Iterable<R>, ? super Iterable<? extends S>> selector) {
	//
	// return new Function<Iterable<R>, Iterable<? extends T>>() {
	// public Iterable<R> invoke(Iterable<? extends T> t) throws Throwable {
	// return selector.invoke(Query.selectMany(transform, t));
	// }
	// };
	// }
	//
	// public static <T, S, R> Function<Iterable<R>, ? super Iterable<? extends
	// T>> from1(
	// final Function<Iterable<S>, ? super T> transform,
	// final Function2<Iterable<R>, ? super Iterable<Join<T, S>>, Class<?>>
	// selector) {
	//
	// return new Function<Iterable<R>, Iterable<? extends T>>() {
	// public Iterable<R> invoke(Iterable<? extends T> t) throws Throwable {
	// return selector.invoke(Query1.selectMany1(t, transform),
	// Void.TYPE);
	// }
	// };
	// }
	//
	// public static <T, Result> Iterable<Join<T, Result>> selectMany1(
	// final Iterable<? extends T> t,
	// final Function<Iterable<Result>, ? super T> transform) {
	// return null;
	// }
	//
	// /*
	// * public static <T, S, R> { T, S => Iterable<R> } join({ T, S => boolean
	// }
	// * joinOn, { T, S => R } joinTo) { return null; }
	// *
	// * public static <T, S, R> { T, S => Iterable<Tuple2<T,S>> } join({ T, S
	// =>
	// * boolean } joinOn) { return null; }
	// *
	// * public static <T, S> Iterable<Tuple2<T,S>> join(Iterable<T> it,
	// * Iterable<S> is, { T, S => boolean } joinOn) { return null; }
	// *
	// * public static <T, S, R> Iterable<R> from(Iterable<T> source1,
	// Iterable<S>
	// * source2, { T, S => boolean } joinOn, { T, S => Iterable<R> } selector)
	// {
	// * return null; }
	// */
	//
	// // /*public*/ static <E> Iterable<E> where(final Iterable<? extends E>
	// // source,
	// // final { E => boolean } predicate) {
	// // return null;
	// // }
	// public static <T, R> Function<Iterable<R>, ? super Iterable<? extends T>>
	// where(
	// final Function<Boolean, ? super T> p,
	// final Function<Iterable<R>, ? super Iterable<? extends T>> selector) {
	// // return { Iterable<? extends T> s => from(where(s, p), selector) };
	// return new Function<Iterable<R>, Iterable<? extends T>>() {
	// public Iterable<R> invoke(Iterable<? extends T> t) throws Throwable {
	// return selector.invoke(Query.where(p, t));
	// }
	// };
	// }
	//
	// public static <T, R> Function<Iterable<R>, ? super Iterable<? extends T>>
	// orderBy(
	// java.util.Comparator<T> c,
	// Function<Iterable<R>, ? super Iterable<? extends T>> selector) {
	// return null;
	// }

	// public static <T, Key, R> { T => Iterable<R> } orderBy(Comparator<T> c, {
	// Group<Key, T> => Iterable<R> } selector) {
	// return null;
	// }

	// public static <T, Key, R> Function<Iterable<R>, ? super Iterable<?
	// extends T>> groupBy(
	// Function<Key, ? super T> keySelector,
	// Function<Iterable<R>, ? super Iterable<? extends Group<Key, T>>>
	// selector) {
	// return null;
	// }

	// /*public*/ static <T, R> Iterable<R> select(final Iterable<? extends T>
	// source,
	// final { T => R } transform) {
	// return null;
	// }

	// public static <T, R> Function<Iterable<R>, ? super Iterable<? extends T>>
	// select(
	// final Function<R, ? super T> transform) {
	// // return { Iterable<? extends T> s => select(s, transform) };
	// return new Function<Iterable<R>, Iterable<? extends T>>() {
	// public Iterable<R> invoke(Iterable<? extends T> t) throws Throwable {
	// return Query.select(transform, t);
	// }
	// };
	// }
	//
	// public static <T, S, R> Iterable<R> select(
	// final Iterable<Join<T, S>> source,
	// final Function2<R, ? super T, ? super S> transform) {
	// return null;
	// }
	//
	// public static <T, S, R> Function2<Iterable<R>, ? super Iterable<Join<T,
	// S>>, Class<?>> select(
	// final Function2<R, ? super T, ? super S> transform) {
	// return new Function2<Iterable<R>, Iterable<Join<T, S>>, Class<?>>() {
	// public Iterable<R> invoke(Iterable<Join<T, S>> t, Class<?> o)
	// throws Throwable {
	// return Query1.select(t, transform);
	// }
	// };
	// }

	// /*public*/ static <T> Iterable<T> skip(final Iterable<T> source, final
	// int count) {
	// return null;
	// }

	// public static <T, R> Function<Iterable<R>, ? super Iterable<? extends T>>
	// skip(
	// final int count,
	// final Function<Iterable<R>, ? super Iterable<? extends T>> selector) {
	// // return { Iterable<? extends T> s => from(skip(s, count), selector) };
	// return new Function<Iterable<R>, Iterable<? extends T>>() {
	// public Iterable<R> invoke(Iterable<? extends T> t) throws Throwable {
	// return selector.invoke(Query.skip(count, t));
	// }
	// };
	// }
}
